﻿/*
 * converted to boost v3:
 * http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/reference.html#Class-directory_iterator
 *
*/

#include <iostream>
#include <fstream>
#include <sstream>
#include <unistd.h> //for unlink()
#include <sys/stat.h> //for mkdir()
#include <string.h> //for strerror(errno)
#include <errno.h> //for strerror(errno)

#include "file-maker.hpp"
#include "../ExeptionHandler/error.hpp"


FileMaker::FileMaker(int argc, char *argv[])
:
// benchmark_directory("/mnt/development/distrefine_working_copy/Benchmarking/trunk/temp"),
benchmark_directory("/tmp/DR_benchmark"),
number_of_files(0)
{
	std::stringstream ss(std::stringstream::in | std::stringstream::out);

	if (mkdir(benchmark_directory.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1)
	{
		/*
		 * If there's an error and the error is not EEXIST,
		 * then we have a problem.
		 */
		if (errno != EEXIST)
		{
			perror("mkdir");
			throw Error("For some reason \"benchmark_directory\" could not be created!");
		}
	}

	//Obviously not concerned if the command line option is correct at this point.
	ss << argv[1];
	ss >> number_of_files;

	//Make a *little* effort to ensure we got a valid number.
	if (number_of_files < 1)
	{
		throw Error("You must provide the number of files to write as the first option!");
	}
	else
	{
		std::cerr << number_of_files << " temporary files will be writen.." << std::endl;
	}
}

#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;

#include "WatchedMountPoint/watched-mountpoint.hpp"
#include "DeviceID/device-id.hpp"
using namespace mounts;

/*
 * TODO: Test if this function does work with multiple pidfiles.
 * Not much error checking here. For example, if the pidfile format
 * is wrong, the function won't work and it's hard to figure out why.
 *
 * I'm not really interested in making this robust right now, since
 * this program is not the focus of my project (Distrefine).
 *
 * NOW, I have to test how this works if the watched dir is "/"
 *
 * CONSIDERING making getMount a class..
*/
std::string FileMaker::getMount()
{
	/*
	 * Going to have to search every pidfile in /var/run/distrefine/
	 * because there could be more than one mount being watched.
	 *
	 * Figured out a much more effecient way to to this:
	 * Just put the mount math in the /var/run/distrefine.pid file
	 * There will still be some coding but probably less
	 */
	try
	{
		path full_path("/var/run/distrefine");
		if (!exists(full_path) || is_regular_file(full_path))
		{
			throw Error("Can't find the DIRECTORY /var/run/distrefine! Has Distrefine been ran yet?");
		}
		else if (is_directory(full_path))
		{
			directory_iterator end_iter;

			/*
			 * Iterate through all files in this directory and open each one ending in .pid .
			 */
			for (directory_iterator dir_itr(full_path); dir_itr != end_iter; ++dir_itr )
			{

				if (dir_itr->path().string().find(".pid")) //could be a problem if the file was about.pid.txt or something.
				{
					WatchedMountPoint wmp(dir_itr->path().string());
					
					DeviceID benchmark_directory_id(benchmark_directory);

					/*
					 * Open the mtab and parse it line by line
					 */
					std::ifstream fs("/etc/mtab");
					string mtab;
					string mount_device;
					string mount_point;
					size_t n= 0;

					while (fs.good())
					{
						getline(fs, mtab);
						
						/*
						 * Looking for lines that start with "/"
						 */
						if (mtab[0] == '/')
						{
							/*
							 * Okay, got the lines I'm interest in. Now,
							 * first part goes into pair.first and second goes
							 * into pair.second.
							 */
							size_t n= mtab.find(' ');
							mount_device= mtab.substr(0 , n);							
							
							/*
							 * Erase the part we just saved, plus the empty space after it.
							 */
							mtab.erase(0 , n+1);
							
							n= mtab.find(' ');
							mount_point= mtab.substr(0 , n);
							
							DeviceID mount_device_id(mount_point);

							if (mount_device_id == benchmark_directory_id)
							{
								cout << "FYI: You are running your benchmark on the mount point: " << mount_point << endl;

								if (wmp == mount_point)
								{
									return string("ENABLED");
								}
							}
						}
					}
					fs.close();
				}
			} //for
		}
		else
		{
			throw Error("Invalid pidfile directory!");
		}
	}
	catch (const filesystem_error& ex)
	{
		cout << ex.what() << '\n';
		throw Error(ex.what());
	}
	
	/*
	 * Did not find a mount.
	 */
	return string("NOT ENABLED");
}

void FileMaker::startTimer()
{
	start_time = times(&start_cpu);
}

void FileMaker::endTimer()
{
	end_time=       times(&end_cpu);
	time_t result=  time(NULL);

	// And print the result to a log file.
	std::string logfile(benchmark_directory+ "/logfile.txt");
	std::ofstream ifs(logfile.c_str(), std::ifstream::in | std::ifstream::app);

	ifs << "------------------------------------ " << asctime(localtime(&result));
	ifs << "* " << number_of_files << " empty files written to:\t" << benchmark_directory << endl;
	ifs << "* Distrefine on this mount is:\t" << getMount() << endl;
	ifs << endl;


	ifs << "Real Time:\t"   << (intmax_t)end_time - start_time                      << std::endl;
	ifs << "User Time:\t"   << (intmax_t)(end_cpu.tms_utime - start_cpu.tms_utime)  << std::endl;
	ifs << "System Time:\t" << (intmax_t)(end_cpu.tms_stime - start_cpu.tms_stime)  << std::endl;
	ifs << endl;

	ifs.close();
}

void FileMaker::removeTemporaryFiles()
{
	std::cout << "Delete all the temporary files we just created? Type no to keep them: ";
	std::string answer;
	std::cin >> answer;
	std::cout << endl;

	if (answer != "no")
	{
		std::stringstream ss(std::stringstream::in | std::stringstream::out);

		for (int i= 0; i < number_of_files; ++i)
		{
			ss << benchmark_directory << "/file." << i;

			cout << "Deleting file: " << ss.str() << endl;

			//And finally, delete it.
			if (unlink(ss.str().c_str()) != 0)
			{
				perror("unlink() failed:");
			}

			//reset
			ss.str("");
		}
	}
}

void FileMaker::makeTemporaryFiles()
{
	std::stringstream ss(std::stringstream::in | std::stringstream::out);
	std::ofstream file;


	startTimer();
	for (int i= 0; i < number_of_files; ++i)
	{
		ss << benchmark_directory << "/file." << i;

		cout << ss.str() << endl;

		file.open(ss.str().c_str());
		file.close();

		ss.str("");
	}
	endTimer();

	removeTemporaryFiles();
}


